using System;
using System.Reflection;

using Microsoft.SPOT;

using OST.Shared;
using OST.Shared.Interfaces;
using OST.Shared.Extensions;

using OST.Persistence;

using mfLogger;
using System.Threading;

namespace OST.Motor
{
    /// <summary>
    /// implementation for <see cref="IMotorProvider"/>
    /// </summary>
    public class MotorProvider : IMotorProvider
    {
        #region CTOR// Singleton
        //http://msdn.microsoft.com/en-us/library/ff650316.aspx
        //--> not thread-safe, with dynamic initialization

        private static MotorProvider _instance = null;

        private MotorProvider()
        {
            
        }

        public static MotorProvider Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new MotorProvider();
                }

                return _instance;
            }
        }

        #endregion

        #region Methods

        void Instance_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "IsConfigApplied" && e.PropertyNewValue != null)
            {
                //TODO: recreate motor-instance, if relevant config entries are changed
                if (MotorInstance == null)
                {
                    CreateMotorInstance();
                }
            }
        }

        private void CreateMotorInstance()
        {
            try
            {
                Logger.Debug("motor-instance creating");

                var config = ConfigurationProvider.Instance.ConfigInstance;
                var motorClassType = Assembly.GetAssembly(typeof(MotorProvider)).GetType(config.Motor.Class);

                if (motorClassType == null)
                {
                    throw new OSTException("motorConfig.MotorClass implementation not found as class");
                }

                //get & invoke default ctor
                Logger.Debug("motor-instance try2 get ctor4 " + config.Motor.Class);
                var objMotor = motorClassType.GetConstructor(new Type[0]).Invoke(new object[0]);
                Logger.Debug("motor-instance created");

                Logger.Debug("motor-instance try2 cast 2 IMotor");
                MotorInstance = objMotor as IMotor;
                if (MotorInstance == null)
                {
                    throw new OSTException("motor-instance " + config.Motor.Class + " is no valid motor implementation");
                }
                Logger.Debug("motor-instance created, starting initialization");

                //init motor async
                var thread = new Thread(new ThreadStart(InitMotorAsync));
                thread.Start();

                OnNotifyPropertyChanged("HasInstance", true);
            }
            catch (Exception ex)
            {
                Logger.Error(ex);
            }
        }

        private void InitMotorAsync()
        {
            MotorInstance.Initialize();
        }

        #endregion

        #region Properties

        /// <summary>
        /// wrapper-property 4 logging
        /// </summary>
        private ILoggerAppender Logger
        {
            get
            {
                return mfLogger.mfLogger.Instance;
            }
        }

        #endregion

        #region IMotorProvider

        public void Initialize()
        {
            ConfigurationProvider.Instance.PropertyChanged += Instance_PropertyChanged;
            Logger.Debug("motor-provider initiated");
        }

        public IMotor MotorInstance
        {
            get;
            private set;
        }

        public bool HasInstance
        {
            get { return MotorInstance != null; }
        }

        #endregion

        #region INotifyPropertyChanged

        /// <summary>
        /// notifies clients over changes in puplic properties
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnNotifyPropertyChanged(String propertyName, object newValue)
        {
            if (PropertyChanged != null && !propertyName.IsNullOrEmpty())
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName, newValue));
            }
        }

        #endregion
    }
}
